#include "QF.hpp"
#include "QAssert.hpp"
#include "QFPkg.hpp"
#include "QFPort.hpp"

namespace QP {

Q_DEFINE_THIS_MODULE("QF")

// Package-scope objects *****************************************************
QSubscrList *QF_subscrList_;
int QF_maxSignal_;

QActive *QF::active_[QF_MAX_ACTIVE + 1];  // to be used by QF ports only

void QF::psInit(QSubscrList *const subscrSto, int maxSignal) {
  QF_subscrList_ = subscrSto;
  QF_maxSignal_ = maxSignal;
}

void QF::add_(QActive *const a) noexcept {
  uint8_t p = a->m_prio;

  Q_REQUIRE((0U < p) && (p <= static_cast<uint8_t>(QF_MAX_ACTIVE)) &&
            (active_[p] == static_cast<QActive *>(0)));

  QF_CRIT_STAT_
  QF_CRIT_ENTRY_();

  active_[p] = a;  // registger the active object at this priority

  QF_CRIT_EXIT_();
}

void QF::remove_(QActive *const a) noexcept {
  uint8_t p = a->m_prio;

  Q_REQUIRE((0U < p) && (p <= static_cast<uint8_t>(QF_MAX_ACTIVE)) &&
            (active_[p] == a));

  QF_CRIT_STAT_
  QF_CRIT_ENTRY_();

  active_[p] = static_cast<QActive *>(0);  // free-up the priority level

  QF_CRIT_EXIT_();
}

void QF::publish_(std::shared_ptr<const QEvt> &e) {
  Q_REQUIRE(static_cast<int>(e->sig) < QF_maxSignal_);

  QF_CRIT_STAT_
  QF_CRIT_ENTRY_();
  QPSet subscrList = QF_PTR_AT_(QF_subscrList_, e->sig);
  QF_CRIT_EXIT_();
  if (subscrList.notEmpty()) {
    uint_fast8_t p = subscrList.findMax();  // the highest-prio subscriber
    do {
      // the prio of the AO must be registered with the framework
      Q_ASSERT_ID(510, active_[p] != static_cast<QActive *>(0));

      // POST() asserts internally if the queue overflows
      (void)active_[p]->POST(e, 0);
      subscrList.remove(p);
      if (subscrList.notEmpty()) {
        p = subscrList.findMax();
      } else {
        p = static_cast<uint_fast8_t>(0);
      }

    } while (p != static_cast<uint_fast8_t>(0));
  }
}

void QF::tick(void) {
  QF_CRIT_STAT_
  QF_CRIT_ENTRY_();

  for (auto iterator = QF_timeEvtListHead_.begin(); iterator != QF_timeEvtListHead_.end();) {
    auto pTimeEvt = (*iterator);
    
    if (pTimeEvt->m_ctr == static_cast<QTimeEvtCtr>(0)) {
      iterator = QF_timeEvtListHead_.erase(iterator);
    } else {
      --pTimeEvt->m_ctr;
      if (pTimeEvt->m_ctr == static_cast<QTimeEvtCtr>(0)) {
        // periodic time event?
        if (pTimeEvt->m_interval != static_cast<QTimeEvtCtr>(0)) {
          pTimeEvt->m_ctr = pTimeEvt->m_interval;
          iterator++;
        } else {
          iterator = QF_timeEvtListHead_.erase(iterator);
        }
        QF_CRIT_EXIT_();  // leave crit. section before posting
                          // POST() asserts internally if the queue overflows
        pTimeEvt->m_act->POST(pTimeEvt, "dummy");
        QF_CRIT_ENTRY_();  // re-enter crit. section to continue
      } else {
        ++iterator;
      }
    }
  }
  QF_CRIT_EXIT_();
}

bool QF::noTimeEvtsActive(void) { return QF_timeEvtListHead_.empty(); }

uint32_t QF::getQueueMargin(uint8_t const prio) {
  Q_REQUIRE((prio <= static_cast<uint8_t>(QF_MAX_ACTIVE)) &&
            (active_[prio] != static_cast<QActive *>(0)));

  QF_CRIT_STAT_
  QF_CRIT_ENTRY_();
  uint32_t margin = static_cast<uint32_t>(active_[prio]->m_eQueue.m_nMin);
  QF_CRIT_EXIT_();

  return margin;
}

/* log-base-2 lookup table **************************************************/
#ifndef QF_LOG2

uint8_t const QF_log2Lkup[256] = {
    static_cast<uint8_t>(0), static_cast<uint8_t>(1), static_cast<uint8_t>(2),
    static_cast<uint8_t>(2), static_cast<uint8_t>(3), static_cast<uint8_t>(3),
    static_cast<uint8_t>(3), static_cast<uint8_t>(3), static_cast<uint8_t>(4),
    static_cast<uint8_t>(4), static_cast<uint8_t>(4), static_cast<uint8_t>(4),
    static_cast<uint8_t>(4), static_cast<uint8_t>(4), static_cast<uint8_t>(4),
    static_cast<uint8_t>(4), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
    static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
    static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
    static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
    static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(5),
    static_cast<uint8_t>(5), static_cast<uint8_t>(5), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(6), static_cast<uint8_t>(6),
    static_cast<uint8_t>(6), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(7),
    static_cast<uint8_t>(7), static_cast<uint8_t>(7), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8), static_cast<uint8_t>(8), static_cast<uint8_t>(8),
    static_cast<uint8_t>(8)};

#endif  // QF_LOG2
}
